﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Data;
using System.Net.Mail;
using System.Web;
using System.Globalization;


namespace CCPSIP
{
    public enum HashType { MD5, SHA1, SHA256, SHA512 };

    public class HashKit
    {
        /// <summary>
        /// Get Hash String
        /// </summary>
        /// <param name="str">Input String</param>
        /// <param name="hashType">Hash 類型</param>
        /// <returns></returns>
        public static string getHashStr(string str, HashType hashType)
        {
            HashAlgorithm hashObj;

            if (hashType == HashType.MD5)
                hashObj = new MD5CryptoServiceProvider();
            else if (hashType == HashType.SHA1)
                hashObj = new SHA1Managed();
            else if (hashType == HashType.SHA256)
                hashObj = new SHA256Managed();
            else if (hashType == HashType.SHA512)
                hashObj = new SHA512Managed();
            else
                return null;

            byte[] hashBytes = hashObj.ComputeHash(Encoding.UTF8.GetBytes(str));
            hashObj.Clear();

            string hashStr = "";
            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashStr += hashBytes[i].ToString("x2");
            }
            return hashStr;
        }


        /// <summary>
        /// 計算檔案的 Hash 值
        /// </summary>
        /// <param name="filePath">檔案路徑</param>
        /// <param name="hashType">Hash 類型</param>
        /// <returns>檔案的 Hash String</returns>
        public static string getFileHash(string filePath, HashType hashType)
        {
            HashAlgorithm hashObj;
            if (hashType == HashType.MD5)
                hashObj = new MD5CryptoServiceProvider();
            else if (hashType == HashType.SHA1)
                hashObj = new SHA1Managed();
            else if (hashType == HashType.SHA256)
                hashObj = new SHA256Managed();
            else if (hashType == HashType.SHA512)
                hashObj = new SHA512Managed();
            else
                return null;

            System.IO.FileStream fileStream = System.IO.File.OpenRead(filePath);
            byte[] hashBytes = hashObj.ComputeHash(fileStream);
            hashObj.Clear();
            fileStream.Close();

            string hashStr = "";
            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashStr += hashBytes[i].ToString("x2");
            }
            return hashStr;
        }


        public static string getHashCode(HashType hashType)
        {
            if (hashType == HashType.MD5)
                return "M1";
            else if (hashType == HashType.SHA1)
                return "S1";
            else if (hashType == HashType.SHA256)
                return "S2";
            else if (hashType == HashType.SHA512)
                return "S3";
            else
                return "S3";
        }

        public static HashType getHashType(string HashCode)
        {
            if (HashCode == "M1")
                return HashType.MD5;
            else if (HashCode == "S1")
                return HashType.SHA1;
            else if (HashCode == "S2")
                return HashType.SHA256;
            else if (HashCode == "S3")
                return HashType.SHA512;
            else
                return HashType.SHA512;
        }

    }

    public class ChrisKit
    {
        public const string kDateTimeFormatDefault = "yyyy/MM/dd HH:mm:ss";

        #region Send Email


        private static MailMessage CreateEmail(string fromAddr, string[] toList, string[] bccList, string subject, string body)
        {
            MailMessage mail = new MailMessage();
            if (toList != null)
            {
                foreach (string toAddr in toList)
                    mail.To.Add(new MailAddress(toAddr));
            }
            if (bccList != null)
            {
                foreach (string bccAddr in bccList)
                    mail.Bcc.Add(new MailAddress(bccAddr));
            }

            //2015/01/01 chris_lin的Notes帳號已刪除
            //mail.Bcc.Add(new MailAddress("chris_lin@ccp.com.tw"));

            if (System.Text.RegularExpressions.Regex.IsMatch(fromAddr, @".+<\S+@\S+\.\S+>$"))
            {
                int addrStartIndex = fromAddr.IndexOf("<") + 1;
                int addrEndIndex = fromAddr.IndexOf(">");
                int addrLength = addrEndIndex - addrStartIndex;
                string senderName = fromAddr.Substring(0, addrStartIndex - 1).Trim();
                string senderAddr = fromAddr.Substring(addrStartIndex, addrLength);
                mail.From = new MailAddress(senderAddr, senderName, System.Text.Encoding.UTF8);
                Console.WriteLine("'{0}'\t'{1}'", senderName, senderAddr);
            }
            else
            {
                mail.From = new MailAddress(fromAddr, fromAddr, System.Text.Encoding.UTF8);
            }

            mail.IsBodyHtml = false;
            mail.BodyEncoding = System.Text.Encoding.UTF8;
            mail.Subject = subject;
            mail.Body = string.Format("Email Send Time: {0}", DateTime.Now.ToString("yyyy/MM/dd HH:mm"))
              + Environment.NewLine + "Powered by Chris Lin."
              + Environment.NewLine + Environment.NewLine
              + body;
            return mail;
        }


        public static void SendCCPEmail(string fromAddr, string[] toList, string[] bccList, string subject, string body)
        {
            MailMessage mail = CreateEmail(fromAddr, toList, bccList, subject, body);
            SmtpClient smtpClient = new SmtpClient("smtp.ccp.com.tw", 25);
            smtpClient.EnableSsl = false;
            smtpClient.Timeout = 60 * 1000;
            smtpClient.Send(mail);
        }


        public static void SendCCPEmail(string fromAddr, string[] toList, string[] bccList, string subject, string body, string[] fileNames)
        {
            MailMessage mail = CreateEmail(fromAddr, toList, bccList, subject, body);
            foreach (string fileName in fileNames)
            {
                Attachment fileItem = new Attachment(fileName);
                fileItem.ContentDisposition.CreationDate = System.IO.File.GetCreationTime(fileName);
                fileItem.ContentDisposition.ModificationDate = System.IO.File.GetLastWriteTime(fileName);
                mail.Attachments.Add(fileItem);
            }
            SmtpClient smtpClient = new SmtpClient("smtp.ccp.com.tw", 25);
            smtpClient.EnableSsl = false;
            smtpClient.Timeout = 60 * 1000;
            smtpClient.Send(mail);
        }

        #endregion


        /// <summary>
        /// 取得連線的 IP Address
        /// </summary>
        /// <param name="req">HttpRequest物件</param>
        /// <returns>IP Address字串</returns>
        public static string getIPAddress(System.Web.HttpRequest req)
        {
            string ipAddress = req.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length > 0)
                {
                    return addresses[0];
                }
            }
            return req.ServerVariables["REMOTE_ADDR"];
        }


        public static string convKeyValue(string symbol, bool isKeyToValue, Dictionary<string, string> dict)
        {
            if (isKeyToValue)
            {
                foreach (KeyValuePair<string, string> item in dict)
                {
                    if (symbol == item.Key)
                        return item.Value;
                }
            }
            else
            {// value to key
                foreach (KeyValuePair<string, string> item in dict)
                {
                    if (symbol == item.Value)
                        return item.Key;
                }
            }
            return null;
        }


        #region String Processing

        public static string Substr(string longStr, uint startIndex, uint length)
        {
            if (startIndex + length > longStr.Length)
                return longStr.Substring((int)startIndex);
            else
                return longStr.Substring((int)startIndex, (int)length);
        }


        public static string QuoteS(string str)
        {
            return "'" + str + "'";
        }

        public static string QuoteD(string str)
        {
            return "\"" + str + "\"";
        }

        public static string getDecimalCommaStr(object numObj)
        {
            decimal numValue;
            try
            {
                numValue = Convert.ToDecimal(numObj);
            }
            catch (Exception ex)
            {
                SkipVar(ex);
                return null;
            }

            if (decimal.Truncate(numValue) == numValue)
            {// 整數
                if (numValue == 0)
                    return "0";
                else
                    return string.Format("###,###,###,###,###", numValue);
            }
            else
            {// 有小數位
                if (numValue >= 10000)
                    return string.Format("###,###,###,###,###", numValue);
                else if (numValue < 1)
                    return string.Format("0.####", numValue);
                else if (numValue < 1000)
                    return string.Format("###.####", numValue);
                else // 10000 > number >= 1000
                    return string.Format("##,###.##", numValue);
            }
        }


        public static string getHexStr(string str)
        {
            byte[] hexBytes = Encoding.UTF8.GetBytes(str);
            string hexStr = "";
            for (int i = 0; i < hexBytes.Length; i++)
            {
                hexStr += hexBytes[i].ToString("x2");
            }
            return hexStr;
        }

        public static string getHexStr(byte[] byteArray, bool reverse)
        {
            string hexStr = "";
            if (reverse)
            {
                for (int i = byteArray.Length - 1; i >= 0; i--)
                    hexStr += byteArray[i].ToString("x2");
            }
            else
            {
                for (int i = 0; i < byteArray.Length; i++)
                    hexStr += byteArray[i].ToString("x2");
            }
            return hexStr;
        }

        public static string GetBinaryStr(byte[] byteArray, bool reverse)
        {
            string binaryStr = "";
            if (reverse)
            {
                for (int i = byteArray.Length - 1; i >= 0; i--)
                    binaryStr += Convert.ToString(byteArray[i], 2).PadLeft(8, '0');
            }
            else
            {
                for (int i = 0; i < byteArray.Length; i++)
                    binaryStr += Convert.ToString(byteArray[i], 2).PadLeft(8, '0');
            }
            return binaryStr;
        }

        public static string ReverseStr(string text)
        {
            if (text == null)
                return null;
            if (text.Length <= 1)
                return text;

            char[] charArray = text.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }

        #endregion


        #region Resource File Handle

        public static System.IO.Stream getResourceStream(string fileName)
        {
            System.Reflection.Assembly asm =
              System.Reflection.Assembly.GetExecutingAssembly();
            string asmName = asm.GetName().Name;
            return asm.GetManifestResourceStream(asmName + "." + fileName);
        }

        public static string getResourceText(string fileName)
        {
            System.IO.StreamReader reader =
              new System.IO.StreamReader(getResourceStream(fileName));
            return reader.ReadToEnd();
        }

        #endregion


        #region Null or Exception Handle

        public static string emptyNull(string inputStr)
        {
            if (inputStr == null)
                return "";
            else
                return inputStr;
        }

        public static string SafeTrim(string inputStr)
        {
            if (inputStr == null)
                return null;
            else
                return inputStr.Trim();
        }

        /// <summary>
        /// Use this function to skip the CS0168 warning.
        /// For example:
        /// "The variable 'ex' is declared but never used"
        /// </summary>
        /// <param name="obj"></param>
        public static void SkipVar(object obj)
        {
            // Use this function to skip unused variable
        }

        public static XmlNode stringToNode(string inputStr)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement node = doc.CreateElement("OnlyString");
            node.InnerText = HttpUtility.HtmlEncode(inputStr);
            return node;
        }

        public static string msg2XmlString(string message)
        {
            return "<Message>" + HttpUtility.HtmlEncode(message) + "</Message>";
        }

        /// <summary>
        /// 將Exception轉成XML格式的字串
        /// </summary>
        /// <param name="ex">要轉換的Exception物件</param>
        /// <param name="includeStackTrace">要不要包含StackTrace</param>
        /// <returns>Exception詳細資訊(XML格式字串)</returns>
        public static string Ex2Xml(Exception ex, bool includeStackTrace)
        {
            string result = "<Exception>"
              + Environment.NewLine + "<Type>" + ex.GetType().Name + "</Type>"
              + Environment.NewLine + "<Message>" + ex.Message + "</Message>";

            if (includeStackTrace)
            {
                result += Environment.NewLine + "<StackTrace>";
                result += HttpUtility.HtmlEncode(ex.StackTrace) + "</StackTrace>";
            }
            result += Environment.NewLine + "</Exception>";
            return result;
        }


        public static string Ex2JSON(Exception ex, bool haveStackTrace)
        {
            Dictionary<string, string> exDict = new Dictionary<string, string>();
            exDict.Add("Type", ex.GetType().Name);
            exDict.Add("Message", ex.Message);
            if (haveStackTrace)
            {
                exDict.Add("StackTrace", ex.StackTrace);
            }
            return DictToJSON(exDict, true);
        }

        #endregion


        #region Dictionary Handle

        public static string DictToJSON(Dictionary<string, string> paramDict)
        {
            return DictToJSON(paramDict, false);
        }

        public static string DictToJSON(Dictionary<string, string> paramDict, bool readable)
        {
            fastJSON.JSON.Instance.Parameters.EnableAnonymousTypes = true;
            fastJSON.JSON.Instance.Parameters.UseEscapedUnicode = false;
            string jsonStr = fastJSON.JSON.Instance.ToJSON(paramDict);
            if (readable)
                return fastJSON.JSON.Instance.Beautify(jsonStr);
            else
                return jsonStr;
        }

        public static Dictionary<string, string> ToDict(System.Collections.Specialized.NameValueCollection nvc)
        {
            Dictionary<string, string> newDict = new Dictionary<string, string>(nvc.Count);
            for (int i = 0; i < nvc.Count; i++)
            {
                newDict[nvc.Keys[i]] = nvc[i];
            }
            return newDict;
        }

        #endregion


        #region New XmlElement Functions

        public static XmlElement newElement(XmlDocument doc, DataRow row, string name)
        {
            XmlElement anElement = doc.CreateElement(name);
            anElement.InnerText = row[name].ToString().Trim();
            return anElement;
        }

        public static XmlElement newElement(XmlDocument doc, DataRow row, string name, Dictionary<string, string> attributes)
        {
            XmlElement anElement = doc.CreateElement(name);
            anElement.InnerText = row[name].ToString();
            foreach (string key in attributes.Keys)
            {
                anElement.SetAttribute(key, attributes[key]);
            }
            return anElement;
        }

        public static XmlElement newElement(XmlDocument doc, string name, string value)
        {
            XmlElement anElement = doc.CreateElement(name);
            anElement.InnerText = value;
            return anElement;
        }

        #endregion


        #region Query String Functions


        public static string DictToQueryStr(Dictionary<string, string> paramDict, Encoding enc)
        {
            string resultStr = "";
            foreach (KeyValuePair<string, string> param in paramDict)
            {
                string encValue = HttpUtility.UrlEncode(param.Value, enc);
                resultStr += string.Format("&{0}={1}", param.Key, encValue);
            }
            return resultStr.Substring(1);
        }


        public static Dictionary<string, string> DictFromQueryStr(string queryStr)
        {
            string[] paramSeparator = { "&" };
            string[] paramArray = queryStr.Split(paramSeparator, StringSplitOptions.RemoveEmptyEntries);

            Dictionary<string, string> resultDict = new Dictionary<string, string>(paramArray.Length);
            foreach (string paramPair in paramArray)
            {
                Match match = Regex.Match(paramPair, @"^[a-zA-Z0-9_\-]+=");
                if (match != Match.Empty)
                {
                    string key = paramPair.Substring(0, match.Length - 1);
                    string value = paramPair.Substring(match.Length);
                    resultDict.Add(key, value);
                }
            }
            return resultDict;
        }


        public static Dictionary<string, string> DictFromKVStr(string kvPairStr, string separator)
        {
            string[] paramSeparator = { separator };
            string[] paramArray = kvPairStr.Split(paramSeparator, StringSplitOptions.RemoveEmptyEntries);

            Dictionary<string, string> resultDict = new Dictionary<string, string>(paramArray.Length);
            foreach (string paramPair in paramArray)
            {
                int eqIndex = paramPair.IndexOf("=");
                string key = paramPair.Substring(0, eqIndex);
                string value = "";
                if (eqIndex < paramPair.Length - 1)
                {
                    value = paramPair.Substring(eqIndex + 1);
                }
                resultDict.Add(key, value);
            }
            return resultDict;
        }


        #endregion



        /// <summary>
        /// 取得 "Content-Disposition" 的合法字串(UTF-8格式)。
        /// 
        /// Test Cases for HTTP Content-Disposition
        /// http://greenbytes.de/tech/tc2231/
        /// </summary>
        /// <param name="filename">檔案名稱</param>
        /// <returns>Content-Disposition 字串</returns>
        public static string getContentDisposition(string filename)
        {
            return "attachment; filename*=UTF-8''" + HttpUtility.UrlEncode(filename);
        }


        /// <summary>
        /// Convert DateTime to Universal sortable date/time string.
        /// </summary>
        /// <param name="anObject">DateTime object or other</param>
        /// <returns>Universal sortable date/time string.</returns>
        public static string toUTCTimeStr(object anObject)
        {
            if (anObject != null && anObject is DateTime)
                return Convert.ToDateTime(anObject).ToUniversalTime().ToString("u");
            else
                return null;
        }


        /// <summary>
        /// 依指定格式把字串轉換為 DateTime
        /// </summary>
        /// <param name="timeStr">時間字串</param>
        /// <param name="format">時間字串格式</param>
        /// <returns>轉換成功回傳 DateTime，否則null</returns>
        public static DateTime? ParseDateTime(string timeStr, string format)
        {
            DateTime dateTime;
            bool success = DateTime.TryParseExact(timeStr, format,
              CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime);
            if (success)
                return dateTime;
            else
                return null;
        }


        /// <summary>
        /// Get random string of given length.
        /// </summary>
        /// <param name="length">Random string length</param>
        /// <param name="exclude01">Should exclude look-alike chars (0,O,1,I,L)</param>
        /// <returns>Random string</returns>
        public static string GetRandomStr(int length, bool exclude01)
        {
            string randomStr = "";
            while (randomStr.Length < length)
            {
                randomStr += System.IO.Path.GetRandomFileName().Remove(8, 1);
                if (exclude01)
                    randomStr = Regex.Replace(randomStr, "[0oO1iIlL]", "");
            }

            if (randomStr.Length > length)
                return randomStr.Substring(0, length);
            else
                return randomStr;
        }


        /// <summary>
        /// Get random string of given length.
        /// </summary>
        /// <param name="length">Random string length</param>
        /// <returns>Random string</returns>
        public static string GetRandomStr(int length)
        {
            return GetRandomStr(length, false);
        }


        #region Calculate GPS Distance


        private static double Radian(double d)
        {
            return d * Math.PI / 180.0;
        }


        public static double Distance(double lat1, double lng1, double lat2, double lng2)
        {
            const double kEarthRadius = 6378137;//地球半徑(公尺)
            double radLat1 = Radian(lat1);
            double radLat2 = Radian(lat2);
            double a = radLat1 - radLat2;
            double b = Radian(lng1) - Radian(lng2);
            double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2)
              + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2)));
            return s * kEarthRadius;
        }


        public static double Distance(decimal lat1, decimal lng1, decimal lat2, decimal lng2)
        {
            double[] pArray = new double[4];
            pArray[0] = Convert.ToDouble(lat1);
            pArray[1] = Convert.ToDouble(lng1);
            pArray[2] = Convert.ToDouble(lat2);
            pArray[3] = Convert.ToDouble(lng2);
            return Distance(pArray[0], pArray[1], pArray[2], pArray[3]);
        }


        #endregion



        /// <summary>
        /// 清理電話號碼中多餘的分隔符號(空白,減號,括號等)
        /// </summary>
        /// <param name="phoneNum">國際電話號碼字串</param>
        /// <returns>乾淨的電話號碼字串</returns>
        public static string CleanPhoneNum(string phoneNum)
        {
            if (string.IsNullOrEmpty(phoneNum))
                return "";

            const string kCleanPattern = @"[\- ()]";
            return Regex.Replace(phoneNum.Trim(), kCleanPattern, "");
        }

        /// <summary>
        /// 清理電話號碼中多餘的分隔符號(空白,減號,括號等), 若傳入空值則回傳空值
        /// </summary>
        /// <param name="phoneNum">國際電話號碼字串</param>
        /// <returns>乾淨的電話號碼字串</returns>
        public static string CleanPhoneNumReturnNull(string phoneNum)
        {
            if (string.IsNullOrEmpty(phoneNum))
                return null;

            const string kCleanPattern = @"[\- ()]";
            return Regex.Replace(phoneNum.Trim(), kCleanPattern, "");
        }

        /// <summary>為了要改存放D槽資料至Unicode-db上而新增的全域變數</summary>
        /// <returns>資料存放位置根目錄</returns>
        //public static string RootDir = "D:\\";
        public static string RootDir = @"\\UNICODE-DB\";

    }// End of class ChrisKit
}
